import { throttle } from 'lodash-es';
import * as THREE from 'three'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.ts';
import { emitter } from '../../utils/mitter'
import Events from './Events';

export type Animate = {
    fun: (arg: any) => any,
    content: any,
}

export class Viewer {

    public id: string
    public scene!: THREE.Scene
    public camera!: THREE.PerspectiveCamera
    public control!: OrbitControls
    public renderer!: THREE.WebGLRenderer
    public viewDom!: HTMLElement
    public raycater!: THREE.Raycaster
    public mouseEvent: MouseEvent | null = null
    public mouse!: THREE.Vector2
    public raycasterObjects: THREE.Object3D[] = []
    public animateEventList: any[] = [];
    public isDestroy: boolean = false;

    constructor(id: string) {
        this.id = id
        this.initScence()
    }

    // 初始化场景
    private initScence() {
        const scene = new THREE.Scene()
        this.scene = scene
    }

    // 坐标辅助
    public addAxis() {
        const axesHelper = new THREE.AxesHelper(1000)
        this.scene.add(axesHelper)
    }

    public addAnimate(animate: Animate) {
        this.animateEventList.push(animate)
    }


    // 初始化相机
    private initCamera() {
        const camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 1, 1000)
        camera.position.set(0, 0, 100)
        camera.lookAt(new THREE.Vector3(0, 0, 0))
        this.camera = camera
    }

    // 初始化相机控制
    private initCameraControl() {
        this.control = new OrbitControls(this.camera, this.renderer.domElement)
        this.control.enableDamping = false // 不启用相机阻尼
        this.control.screenSpacePanning = false // 不启用水平滚动
        this.control.minDistance = 2 // 最小距离
        this.control.maxDistance = 1000
        this.renderer.render(this.scene, this.camera)
    }

    private initViewer() {
        this.initRenderer()
        this.initScence()
        this.initCamera()
        this.initLight()
        this.initCameraControl()

        this.raycater = new THREE.Raycaster();
        this.mouse = new THREE.Vector2();

        const animate = () => {
            if (this.isDestroy) return;
            requestAnimationFrame(animate);

            this.updateDom();
            this.readerDom();

            // 全局的公共动画函数，添加函数可同步执行
            this.animateEventList.forEach(event => {
                // event.fun && event.content && event.fun(event.content);
                if (event.fun && event.content) {
                    event.fun(event.content);
                }
            });
        };

        animate();
    }

    private updateDom() {
        this.control.update();

        this.camera.aspect = this.viewDom.clientWidth / this.viewDom.clientHeight
        this.camera.updateProjectionMatrix()
        this.renderer.setSize(this.viewDom.clientWidth, this.viewDom.clientHeight)
        this.renderer.setPixelRatio(window.devicePixelRatio)
    }

    // 初始化灯光
    private initLight() {
        const ambientLight = new THREE.AmbientLight(0xffffff, 0.6)
        this.scene.add(ambientLight)

        const ligth = new THREE.DirectionalLight(0xffffff, 0.6)
        ligth.position.set(0, 100, 0)
        ligth.castShadow = true // 可以投射阴影

        ligth.shadow.camera.top = 180
        ligth.shadow.camera.bottom = -100
        ligth.shadow.camera.left = -120
        ligth.shadow.camera.right = 120
        ligth.shadow.camera.near = 1
        ligth.shadow.camera.far = 500
        ligth.shadow.mapSize.set(1024, 1024)
        this.scene.add(ligth)
    }

    // 注册鼠标事件的监听
    public initRaycaster() {
        this.raycater = new THREE.Raycaster()

        const initRaycasterEvent: Function = (eventName: keyof HTMLMediaElementEventMap): void => {
            const funWarp = throttle((event: any) => {
                this.mouseEvent = event
                this.mouse.x = (event.clientX / window.innerWidth) * 2 - 1
                this.mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
                // @ts-expect-error
                emitter.emit(Events[eventName].raycaster, this.getRaycaterIntersectObjects(eventName));
            }, 50)

            this.viewDom.addEventListener(eventName, funWarp, false)
        }
        initRaycasterEvent('click')
        initRaycasterEvent('dblclick')
        initRaycasterEvent('mousemove')
    }

    // 渲染dom
    private readerDom() {
        this.viewDom = document.getElementById(this.id) as HTMLElement
    }

    // 初始化渲染器
    private initRenderer() {
        // 获取画布dom
        const canvas = document.getElementById(this.id) as HTMLCanvasElement

        // 初始化渲染器
        this.renderer = new THREE.WebGLRenderer({
            logarithmicDepthBuffer: true, // 启用深度缓冲区
            canvas: canvas,
            antialias: true, // 开启抗锯齿
            alpha: true, // 背景透明
            precision: 'mediump', // 设置渲染精度
            premultipliedAlpha: true, // true/false 表示是否可以设置像素深度（用来度量图像的分辨率）
        })

        this.renderer.clearDepth()

        this.renderer.shadowMap.enabled = true // 显示阴影
        this.renderer.outputColorSpace = THREE.SRGBColorSpace // 可以看到更亮的材质，同时这也影响到环境贴图
        this.viewDom.appendChild(this.renderer.domElement)
    }

    /**自定义鼠标事件触发的范围，给定一个模型组，对给定的模型组鼠标事件才生效 */
    public setRaycasterObjects(objList: THREE.Object3D[]): void {
        this.raycasterObjects = objList
    }

    private getRaycaterIntersectObjects() {
        if (!this.raycasterObjects.length) return []
        this.raycater.setFromCamera(this.mouse, this.camera)
        return this.raycater.intersectObjects(this.scene.children)
    }
}